package heuristic;

import es.deusto.ingenieria.is.search.algorithms.Node;
import es.deusto.ingenieria.is.search.algorithms.heuristic.EvaluationFunction;
import es.deusto.ingenieria.is.search.algorithms.heuristic.HeuristicSearchMethod;
import es.deusto.ingenieria.is.search.formulation.Operator;
import es.deusto.ingenieria.is.search.formulation.Problem;
import es.deusto.ingenieria.is.search.formulation.State;

public class HillClimbing extends HeuristicSearchMethod
{
	private int numNodos;
	public int getNodos()
	{
		return numNodos;
	}
	public void setNodos(int numNod)
	{
		this.numNodos = numNod;
	}

	public HillClimbing(EvaluationFunction function)
	{
		super(function);
	}

	@Override
	public Node search(Problem problem, State initialState)
	{
		Node firstNode = new Node(initialState);
		Node currentNode = firstNode;
		Node bestSuccessor = firstNode;
		
		boolean localBestFound = false;
		
		currentNode.setH(this.getEvaluationFunction().calculateH(currentNode));
		
		while (!localBestFound) 
		{			
			bestSuccessor = this.expand(currentNode, problem);
			
//			System.out.println("A-Currentnode: " + currentNode.getH());
//			System.out.println("A-BestSuccessor: " + bestSuccessor.getH());
			
			if (currentNode.getH() <= bestSuccessor.getH())
			{
				localBestFound = true;
			}
			else
			{
				currentNode = bestSuccessor; 
			}
		}
		
		if (localBestFound) 
		{
			return currentNode;
		} 
		else 
		{
			return null;
		}
	}
	
	protected Node expand(Node node, Problem problem) 
	{
		Node successorNode = null;
		Node bestSuccessor = null;
		State currentState = null;
		State successorState = null;
		
//		List<Node> expandedStates = new ArrayList<Node>();
		
		if (node != null && problem != null)
		{
			currentState = node.getState();
			
			if (currentState != null)
			{
				bestSuccessor = node;
				
				for (Operator operator : problem.getOperators())
				{
					successorState = operator.apply(currentState);
					
					if (successorState != null) 
					{
						successorNode = new Node(successorState);
						successorNode.setOperator(operator.getName());
						successorNode.setParent(node);
						successorNode.setDepth(node.getDepth() + 1);
						successorNode.setH(this.getEvaluationFunction().calculateH(successorNode));
						this.setNodos(this.getNodos() + 1);
						System.out.println("Nodos: " + this.getNodos());
//						System.out.println("Successor node: " + successorNode.getH());
//						System.out.println("BestSuccessor node: " + bestSuccessor.getH());
						
						if (successorNode.getH() < bestSuccessor.getH())
						{
							bestSuccessor = successorNode;
						}		
					}	
				}			
			}
		}

		return bestSuccessor;		
	}
}
